iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
Modern Web

Dive into CSS Challenge:從問題到解決方案的實踐之旅系列 第 24

Day 24 - CSS Challenge #13:User Gallery(中)

  • 分享至 

  • xImage
  •  

題目

CSS Challenge Day13

  • Gallery
    https://ithelp.ithome.com.tw/upload/images/20241001/20169403lLGp6QVK44.png

題目除了基本 Gallery 介面樣式以外,還有點擊後的介面

  • Info
    https://ithelp.ithome.com.tw/upload/images/20241001/201694033LVKQfppCu.png

上面的圖是題目,而我們要做出幾乎一樣的樣子,題目中還有附上出題官方的CodePen,也有附上給我們解題用的template,當我們真的不會的時候,還是可以參考他們的寫法,所以沒有想像中困難。

我做好的此題CSS Challeage解答

那麼我們就開始吧。

題目分析

這個題目要求我們製作一個 User Gallery

  1. 主頁面
    • 4 張照片組成
    • 滑鼠指上效果
      • 照片壓黑
      • 動態出現 [+] 的按鈕引導點擊。
  2. Info 頁面:
    • 上下開合的動畫來顯現info,動畫在背景、照片、關閉按鈕都有延遲。
    • 資訊區塊:照片與名稱
    • Action按鈕:下方三顆互動按鈕
    • 關閉按鈕:右上角關閉按鈕

前情提要

我們昨天已經把最開始的四格 User Gallery 跟滑鼠指上的效果都做出來了如下圖,現在我們就來做點擊後要打開的 info 畫面。
https://ithelp.ithome.com.tw/upload/images/20241002/20169403DrJJUO9V7h.png

開始解題

Info

基礎架構

<div class="frame">
    ...
	<div class="info">
		<div class="panel>
            <div class="eachPanel" id="panel-1">
                <div class="header">
                    <img src="..." />
                </div>
                <img class="profile" src="..." />
                <div class="close"></div>
                <div class="bottom">...</div>
            </div>
		</div>
		...
	</div>
</div>

gallery 下面,我們新增另一個區塊,叫做 info

因為我希望點擊 pic-1 的時候,能夠開啟 #panel-1 ,依此類推去開啟不同的 panel(雖然題目沒有做到這個程度),那這邊我的 HTML 就以 #panel-1 來做示範,後面的其他 panel 就依此類推。

這邊我會把 #panel-1#panel-4 都加上一個名為 eachPanel 的 class 並包在 .panel 內,這是為了定位的問題。這四個 div 到時候會是 panel 內的唯一物件,一次只會顯示其中一個。

讓我說明一下:
info 本人會是 position: absolute 的狀態,這樣他才能蓋在 gallery 上面。
panel 則會是 position: relative 的狀態,這樣他裡面的那些元素們,才能在範圍內飛來飛去。有些人會在這邊包了四個 panel 甚至是使用 display: none 來包裝他們,但使用 display 並不能製造出位移的 transition,因此必須使用 opacity: 0 來控制顯示。

當使用了 opacity 來控制顯示,就代表這四個 div 在網頁中都有佔空間,panel-4 會被擠到很下面去根本看不到,因此這邊才會需要把它們都包在同一個 panel 內,吃同一個定位。

開版

另外,題目上可以看出來,當我點擊 Gallery 的圖片之後,info 的內容是從上下位移進來蓋住 Gallery 畫面的,因此我可以在這邊把畫面區分成上下兩塊 div,分別取名為 headerbottom

再觀察題目,可以發現飛進來的動畫分成四個部分:

  1. header 從畫面上飛進來。
  2. bottom 資訊區塊,從畫面下面飛進來。
  3. 照片由上往下飛進來。
  4. 右上角關閉按鈕也是從上往下飛進來。

而且這四個動畫都有些許的順序,也就是它們在 click 之後,各自有先延遲一段不同的長度才開始動畫。

因此我們可以知道,這幾個項目要做在同一層比較妥當(彼此都不要成為彼此的父項)

所以 header 的部分按照題目的,包了題目提供的圖片。
接著是照片跟右上角的關閉按鈕,然後是 bottom 內放上應該要有的資訊項目跟 action button。

這樣我們基礎架構就完成了。

Icons

這邊的按鈕 icon 一樣是使用 css challenge 官方的 font-awesome 來製作,當然你也可以用自己的。
這邊可以看到我的關閉按鈕並沒有使用 font-awesome,是因為他的按鈕的樣式不符合題目要的,他的樣式比較粗也比較小,我試著調整過,但沒有讓我滿意,我希望他能跟 gallery 上面的 "+" 按鈕一樣的樣子,所以我這邊決定把 close icon 用跟 gallery 一樣的方法重做,因此這邊沒有使用 font-awesome 的 icon 來製作。
https://ithelp.ithome.com.tw/upload/images/20240919/20169403YoyNAOV29v.png

Info的樣式

.info {
	z-index: 5;
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	pointer-events: none;
	overflow: hidden;
	font-size: 0;
	
	&.active {
		pointer-events: all;
    }
}

那我們就先來做點擊之後顯示或隱藏的部分,我想使用 info 來控制,那麼我們就先來設計展開的 info 的樣子。

  • z-index: 5:設置 .info 的堆疊順序,使其位於其他層之上。
  • position: absolute top: 0 left: 0 right: 0 bottom: 0:確保 .info 元素完全覆蓋其父容器,填滿所有空間。
  • pointer-events: none:禁用滑鼠事件,因為這個區塊其實是永遠在最上層的,只是看的到或看不到。但也因為它在上層,我們必須隱藏他的滑鼠事件,否則下面 gallery 那一層的滑鼠事件就永遠不會被點到。因此我們先把它的滑鼠事件隱藏起來,這樣元素不會干擾點擊操作。
  • overflow: hidden:隱藏超出容器的內容。
  • .active:這是為了之後動畫作的,我預計讓 gallery 被點擊之後,在 info 的同一個 div 上加上 .active 這個 class。當 .info 被觸發時,通過 pointer-events: all 恢復滑鼠事件。

Panel

.panel {
	position: relative;
	width: 100%;
	height: 100%;
}

這邊我就先設定好 panel 的基本樣式。

那剛做完的時候長這樣:
https://ithelp.ithome.com.tw/upload/images/20241004/20169403NWd0KPombA.png

接下來我們就一個個拆解內部的元素

變數

$headerH: 180px;
$bottomH: 400 - $headerH;

這邊先把一些會用到的元素計算出來:

  • $headerH:這是用我們第一天教的尺規小工具量出來的 header 高度。
  • $bottomH:這是用我們的 .frame 的高度剪掉 $headerH 就能得出下半截 bottom 的高度了。

header

.panel {
    .header {
        position: absolute;
		height: $headerH;
		z-index: 6;
		overflow: hidden;
		transform: translate3d(0,0,0)
        transition: all .6s ease-in .4s;
			img {width: 100%; height: 100%;}
	}
}
  • position: absolute:將 .header 定位於父元素內部的絕對位置。
  • height: $headerH:設定 .header 的高度,值取自變數 $headerH
  • z-index: 6:確保 .header 具有較高的堆疊順序,我這邊是讓它放在 info 之上。
  • overflow: hidden:隱藏任何超出 .header 容器的內容。
  • transform: translate3d(0,0,0):這邊我們看到的是最後的位置,這邊之後是要放在 .active 內的,但沒關係,我們這邊先這樣做,之後再跟 active 內的位置交換。
  • img {width: 100%; height: 100%;}:確保 .header 內的圖片充滿整個容器並保持比例。

bottom

.panel {
    ...
	.bottom {
		@extend .header;
		height: $bottomH;
		background: $primary;
		top: $headerH;
		left: 0;
		right: 0;
        padding-top: 50px;
		box-sizing: border-box;
        transform: translate3d(0,0,0);
	}
}

因為 bottom 很多屬性都跟 header 重複,所以這邊我直接 @extendheader 的屬性,然後我們再來做局部修改。

  • height: $bottomH:設定 .bottom 的高度,使用變數 $bottomH
  • background: $primary:將 .bottom 的背景色設置為主色,取自變數 $primary
  • top: $headerH:將 .bottom 定位於 .header 的正下方,使用變數 $headerH 來確定位置。
  • left: 0 right: 0:確保 .bottom 在水平方向上填滿整個容器。
  • transform: translate3d(0,0,0):這邊我們看到的是最後的位置,這邊之後是要放在 .active 內的,但沒關係,我們這邊先這樣做,之後再跟 active 內的位置交換。
  • padding-top:將區塊上方邊界留空,好讓名字的文字不會貼在 .bottom 的最上方
  • box-sizing:因為設定了 padding,這邊高度會超出預期,所以使用 border-box,讓整體仍然維持在本來期望的高度,這樣等等跑動畫的時候才不會有不預期的情形發生。

bottom 內架構

<div class="bottom">
	<p class="name">Julia Toth</p>
	<div class="action">
		<button><span class="fa fa-phone"></span></button>
		<button><span class="fa fa-comment"></span></button>
		<button><span class="fa fa-heart"></span></button>
	</div>
</div>

這邊是 bottom 內的架構,我們就在這邊先開版。
因為這些東西會跟著 bottom 一起移動,上面三個按鈕也只有滑鼠指上效果,所以相對單純。

名字

.name {
	font-size: 16px;
	font-weight: 600;
	text-align: center;
}

首先是姓名,剛修改好樣式的話是長這樣:
https://ithelp.ithome.com.tw/upload/images/20241004/20169403UQ4Aympxit.png

按鈕區塊

.action {
	display: flex;
	margin: 0 auto;
	justify-content: space-between;
	width: 50%;
	padding-top: 15px;
}

然後是三個按鈕外的 div,我使用它來讓三顆按鈕水平對齊,並使用 padding 創造出與上面的名字的距離,也使用 justify-content: space-betweenwidth 創造三顆按鈕之間的距離。

按鈕本人

$actionIconWH: 45px;

button {
	width: $actionIconWH;
	height: $actionIconWH;
	border-radius: 50%;
	background-color: $primary;
	border: 1px solid #fff;
	color: #fff;
    transition: all .2s ease-in-out;
    cursor: pointer;
    &:hover {
        color: $primary;
        background-color: #fff;
    }
}

然後我們可以來做 button 的部分,我直接就寫在 button 上。

  • $actionIconWH:建立一個新的變數去控管 button 的寬高。

  • 大小與形狀:

    • width: $actionIconWH height: $actionIconWH:按鈕的寬高為 45px(變數 $actionIconWH 的值)。
    • border-radius: 50%:將按鈕設置為圓形。
  • 顏色與視覺樣式:

    • background-color: $primary:背景色使用 $primary 主色。
    • border: 1px solid #fff:邊框為 1px 的白色實線。
    • color: #fff:文字顏色為白色。
  • 滑鼠指上效果:

    • transition: all .2s ease-in-out:所有樣式變化在 0.2 秒內平滑過渡。
    • &:hover:當滑鼠懸停時,按鈕背景變為白色,字體顏色變為 $primary 主色。

https://ithelp.ithome.com.tw/upload/images/20241004/20169403SeAUKXUUWy.png

這樣三個 button 的樣式就差不多了,那我們就來調整裡面 icon。

按鈕的 icon

button {
    ...
	.fa {
		line-height: 20px;
		text-align: center;
        font-size: 20px;
	}
}

我們先簡單粗暴的把 .fa 這個 icon 的 style 加上字體大小,設定讓它在按鈕中間置中,也設定了他的行高,但很快的就會看出不對勁。

https://ithelp.ithome.com.tw/upload/images/20241004/20169403bDb6mq9qWS.png

可以看到雖然有 icon 了,但每個 icon 的大小不一樣,高低也不太相同。

這是因為舊款的 font-awesome 產生出來的 icon 並不會每個都一樣大,而且他們其實是一種字體!
你可以去網頁上按右鍵,選檢查,點到那個 icon 就可以看到他其實是一個偽類,內容是一個字,早期的 font-awesome 是這樣給他一個字體,去讓它呈現 icon 的樣子。

https://ithelp.ithome.com.tw/upload/images/20241004/20169403ZBNRa9vbaZ.png

https://ithelp.ithome.com.tw/upload/images/20241004/20169403oiTr2xbfoF.png

這時候我們能去做的,就是修改這個字:

button {
    ...
	.fa {
		line-height: 20px;
		text-align: center;
		&-phone { 
            font-size: 22px;
            padding-top: 5px;
            padding-left: 1px;
            }
		&-comment {
            font-size: 20px;
            padding-bottom: 2px
        }
		&-heart {
            font-size: 19px;
            padding-top: 4px;
            padding-left: 1px;
        }
	}
}

針對每個 icon 修改它的字型大小,在需要微調的地方加上不同方向的 padding,讓他們在視覺上儘量做到一制,在視覺上不會有大大小小或偏移的感覺就可以了。
目前剛做好的話長這樣:

https://ithelp.ithome.com.tw/upload/images/20241004/20169403gXxWTeXM4I.png

由於篇幅太長了,我決定分兩篇寫,下一篇再來寫後面的照片、右上角關閉按鈕、JS開關介面及動畫的部分。


Wrap up and go home

希望改變了這種按照步驟的寫法,能讓更多人看得懂,也能跟我一樣喜歡上寫CSS。

那今天就先到這裡,明天我們再繼續來玩下一集。


上一篇
Day 23 - CSS Challenge #13:User Gallery(上)
下一篇
Day 25 - CSS Challenge #13:User Gallery(下)
系列文
Dive into CSS Challenge:從問題到解決方案的實踐之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言